7. Text-Mining

7.1. Vorbereitung

Installation der benötigten R-Pakete

install.packages(c("dplyr", "gutenbergr", "stringr", "tidytext", "tidyr", "stopwords", "wordcloud", "rsample", "glmnet", "doMC", "forcats", "broom", "igraph", "ggraph", "yardstick", "kableExtra", "readr", "widly", "wordcloud2", "SnowballC", "webshot", "reshape2"))
webshot::install_phantomjs()

Laden der Bibliotheken

library(DBI)
library(dplyr)
library(forcats)
library(ggplot2)
library(ggraph)
library(grid)
library(gridExtra)
library(htmlwidgets)
library(igraph)
library(knitr)
library(kableExtra)
library(readr)
library(SnowballC)
library(stopwords) 
library(stringr)
library(tibble)
library(tidyr)
library(tidytext)
library(webshot)
library(widyr)

Auslesen der Daten

KillDbConnections <- function () {
  all_cons <- dbListConnections(PostgreSQL())
  for(x in all_cons) +  dbDisconnect(x)
}

ReadPresseTablebyYear <- function(table_name, year) {
  # Initierung der Datenbank
  con <- dbConnect(RPostgreSQL::PostgreSQL(),
    host = 'hdm-sql.think-data.de', 
    dbname = 'postgres',
    user = 'postgres',
    password = '%%CENSORED%%'
    # Der richtige Weg, um auf sichere Weise das Passwort abzufragen
    #password = rstudioapi::askForPassword("Database password")
    )
  
  # Konstruktion der Query
  query <- paste("SELECT * FROM ", table_name, " WHERE filename LIKE '%", year, "%'",sep="")

  # Ausführen der Query und Zurückgeben des Ergebnisses
  return(dbGetQuery(con, query))
  
  # Verbindungsabbau
  KillDbConnections()
}
ReadPresseTable <- function(table_name) {
  # Initierung der Datenbank
  con <- dbConnect(RPostgreSQL::PostgreSQL(),
    host = 'hdm-sql.think-data.de', 
    dbname = 'postgres',
    user = 'postgres',
    password = '%%CENSORED%%'
    # Der richtige Weg, um auf sichere Weise das Passwort abzufragen
    #password = rstudioapi::askForPassword("Database password")
    )

  # Auslesen der Tabelle und zurückgeben des Ergebnisses
  return(dbReadTable(con, table_name))
  
  # Verbindungsabbau
  KillDbConnections()
}

# Daten über die Funktion einlesen
presse2009 <- ReadPresseTablebyYear("presse", 2009)
presse <- ReadPresseTable("presse")

kable(head(presse,1)) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  scroll_box(width = "100%", height = "200px")
id header text publisher author link filename year path
1

Die Berliner waehlen in Lindgruen und Rosa

Zweitstimme ist die wichtige Stimme: Sie entscheidet ueber die tatsaechliche Prozentzahl und ueber die Zahl der Sitze im Parlament/ Erststimme bestimmt den Direktkandidaten in einem Wahlkreis/ Fuer die Bundestagswahl gibt es einen 3. Zettel
TAZ-Bericht Berlin. Ob das eine versteckte Koalitionsaussage ist? Zufall oder nicht, die BerlinerInnen duerfen bei den Wahlen zum Gesamtberliner Abgeordnetenhaus ausgerechnet rosafarbene und lindgruene Stimmzettel ausfuellen. Auf dem rosa Stimmzettel wird die Erststimme abgegeben, auf dem gruenen die Zweitstimme. Entgegen einem weit verbreiteten Irrtum ist die Zweitstimme die wichtigere, denn sie entscheidet ueber die tatsaechliche Prozentzahl und damit ueber die Zahl der Sitze im Parlament. Mit der Erststimme werden die DirektkandidatInnen der einzelnen Wahlkreise gewaehlt, mit der Zweitstimme Bezirks- oder Landeslisten der Parteien. Ein Direktmandat erhaelt, wer die meisten Stimmen in einem Wahlkreis erringt. Die turnusmaessig wiederkehrende Verwirrung vor Wahlen beim sogenannten Wahlvolk hat sich in diesem Jahr noch dadurch gesteigert, dass die Parteien im Sommer monatelang um die Modalitaeten zur ersten gesamtdeutschen Wahl gestritten haben, an die das Berliner Wahlgesetz angelehnt wurde. Nach der Klage von PDS und den Gruenen vor dem Bundesverfassungsgericht in Karlsruhe musste das erste Wahlgesetz zurueckgezogen werden, das eine allgemeine 5-Prozent-Klausel im gesamten Wahlgebiet und das sogenannte Huckepackverfahren vorsah. Im zweiten Wahlgesetz wurde dann die sogenannte regionalisierte 5-Prozentklausel eingefuehrt. Sie bedeutet, dass zur Bundestagswahl eine Partei entweder im Gebiet der alten oder der neuen Bundeslaender die Sperrklausel ueberwinden muss, um ins Parlament einzuziehen. Im Gebiet der Ex-DDR wurden ausserdem Listenvereinigungen von mehreren Gruppierungen oder Parteien zugelassen, um die Chancen der Buergerbewegungen zu erhoehen. Auch fuer die Wahlen zum Abgeordnetenhaus gelten diese Bedingungen: Ost- und West-Berlin bilden zwar ein einheitliches Wahlgebiet, aber zwei verschiedene Zaehlgebiete. Wer es in einem der beiden Teile schafft, die Sperrklausel zu ueberwinden, sitzt automatisch im Parlament. Die Verwirrung, vor allem in der linksalternativen Waehlerschaft, ist deshalb besonders gross, weil sowohl fuer den Bundestag als auch fuer das Abgeordnetenhaus in beiden Teilen der Stadt die AL, Buendnis 90/Gruene und PDS gewaehlt werden kann. Die PDS hat sich im Sommer bundesweit mit der westlichen Linken Liste/PDS vereinigt und tritt deswegen wie die grossen Parteien CDU und SPD an. Die gruenen Parteien wollten diesen Schritt noch nicht vollziehen, haben in Berlin aber die Moeglichkeit, getrennte Landeslisten fuer das gesamte Wahlgebiet aufzustellen - was auch beide getan haben. Konkret heisst das, man kann sowohl in Schoeneberg wie in Friedrichshain mit der Zweitstimme AL und Buendnis 90 waehlen. Die Stimme ist dann nicht verloren, wenn es die beiden Parteien schaffen, in einem der beiden Zaehlgebiete ueber die Sperrklausel zu kommen - wovon trotz des unklaren Wahlausgangs auszugehen ist. Ob man mit der Erststimme einen Direktkandidaten waehlen kann, haengt davon ab, ob die entsprechende Partei im eigenen Wahlkreis ueberhaupt KandidatInnen nominiert hat. In Berlin koennen insgesamt 200 KandidatInnen in 120 Wahlkreisen gewaehlt werden. Das Berliner Abgeordnetenhaus ist damit das groesste Landesparlament der Republik, dessen Parlamentarierzahl noch durch sogenannte Ueberhangsmandate steigen wird. Ihren Sitz werden die Abgeordneten fuer eine Uebergangsfrist von etwa zwei Jahren weiter im Schoeneberger Rathaus haben. Der an sich zu kleine Plenarsaal wird bis zur konstituierenden Sitzung der neuen Regierung am 11. Januar 1991 notduerftig umgeruestet. Erstmals in der Nachkriegsgeschichte duerfen sich die BerlinerInnen auch an Wahlen zum Bundestag direkt beteiligen, d.h. jedeR darf insgesamt vier Kreuzchen machen. Fuer die Bundestagswahl gibt es einen dritten Stimmzettel, auf dem Erst- und Zweitstimme abgegeben werden. In der oeffentlichen Wahrnehmung in Berlin ist diese Wahl gegenueber den Abgeordnetenhauswahlen so gut wie untergegangen, ueberlagert vom Wahlkampf um die Sitze im Abgeordnetenhaus und die kuenftige Landesregierung. kd TAZ-BERLIN Nr. 3275 vom 01.12.1990 Seite 37 kd https://www.wiso-net.de/document/TAZH__T901201.203 Presse_1990_01_Html.htm 1990 Pressetexte/Presseartikel 1990

Auslesen der Daten mit Dplyr

Dplyr hat auch mittlerweile Funktionen, um Daten von PostgreSQL zu lesen.

KillDbConnections <- function () {
  all_cons <- dbListConnections(PostgreSQL())
  for(x in all_cons) +  dbDisconnect(x)
}

ReadData <- function(table_name, year) {
  # Initierung der Datenbank
  con <- src_postgres(
    host = 'hdm-sql.think-data.de', 
    dbname = 'postgres',
    user = 'postgres',
    password = '%%CENSORED%%'
    # Der richtige Weg, um auf sichere Weise das Passwort abzufragen
    #password = rstudioapi::askForPassword("Database password")
    )
  
  # Konstruktion der Query
  query <- paste("SELECT * FROM ", table_name, " WHERE filename LIKE '%", year, "%'",sep="")

  # Ausführen der Query und Zurückgeben des Ergebnisses
  return(tbl(con, sql(query)))
  
  # Verbindungsabbau
  KillDbConnections()
}

# Daten über die Funktion einlesen
presse2009_dplyr <- ReadData("presse", 2009)

Transformation der Daten

Zuerst werden die Texte über die Publikationen angepasst.

# Anpassen der Daten
presse_cleaned <- presse
# Filtern von Inhalten wie "$publikation vom 01.01.2001 ..."
presse_cleaned$publisher <- gsub(" vom.*$", "", presse_cleaned$publisher, ignore.case = TRUE)
# Filtern von Inhalten wie "$publikation print: Nr. 1 ..."
presse_cleaned$publisher <- gsub(" print: nr. \\d.*$", "", presse_cleaned$publisher, ignore.case = TRUE)
# Filtern von Inhalten wie "$publikation Nr. 1 ..."
presse_cleaned$publisher <- gsub(" nr. \\d.*$", "", presse_cleaned$publisher, ignore.case = TRUE)
# Filtern von Inhalten wie "$publikation, Jg. 52, 01.01.2001 ..."
presse_cleaned$publisher <- gsub(", Jg. \\d.*$", "", presse_cleaned$publisher, ignore.case = TRUE)
# Filtern von Inhalten wie "$publikation, 01.01.2001, S. 1 ..."
presse_cleaned$publisher <- gsub(", \\d\\d.\\d\\d.\\d\\d\\d\\d.*$", "", presse_cleaned$publisher, ignore.case = TRUE)
# Filtern von Inhalten wie "$publikation Nr. ..."
presse_cleaned$publisher <- gsub("Nr. *$", "", presse_cleaned$publisher, ignore.case = TRUE)

# Vereinheitlichen der Groß- und Kleinschreibung
# Entfernen aller Leerzeichen vor und nach den Publikationen
presse_cleaned$publisher <- tolower(presse_cleaned$publisher) %>% trimws()

Anschließend werden die Umlaute ä, ö, ü und ß vereinheitlicht, da die Artikel zum einen “ü” als “ue” und zum anderen “ü” als “ü” darstellen. Um eine Verzerrung - beispielsweise im Fall der Partei ‘Die Grünen’ - auszuschließen, wird die Schreibweise “ue” für alle Inhalte vereinheitlicht.

presse_cleaned$header <- gsub("ß", "ss", presse_cleaned$header, ignore.case = TRUE)
presse_cleaned$text <- gsub("ß", "ss", presse_cleaned$text, ignore.case = TRUE)

presse_cleaned$header <- gsub("ä", "ae", presse_cleaned$header, ignore.case = TRUE)
presse_cleaned$text <- gsub("ä", "ae", presse_cleaned$text, ignore.case = TRUE)

presse_cleaned$header <- gsub("ö", "oe", presse_cleaned$header, ignore.case = TRUE)
presse_cleaned$text <- gsub("ö", "oe", presse_cleaned$text, ignore.case = TRUE)

presse_cleaned$header <- gsub("ü", "ue", presse_cleaned$header, ignore.case = TRUE)
presse_cleaned$text <- gsub("ü", "ue", presse_cleaned$text, ignore.case = TRUE)

7.2. Text Mining

Tokenisierung

# Extraktion der Token aus den Artikelüberschriften
token_header <- unnest_tokens(as_tibble(presse_cleaned), word, header, token = "words", format = "text", to_lower = TRUE, drop = TRUE)

# Extraktion der Token aus den Artikeltexten
token_text <- unnest_tokens(as_tibble(presse_cleaned), word, text, token = "words", format = "text", to_lower = TRUE, drop = TRUE)

# Visualisierung der Ergebnisse der Prozeduren
kable(head(token_header,1)) %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  scroll_box(width = "100%", height = "200px")
id text publisher author link filename year path word
1 TAZ-Bericht Berlin. Ob das eine versteckte Koalitionsaussage ist? Zufall oder nicht, die BerlinerInnen duerfen bei den Wahlen zum Gesamtberliner Abgeordnetenhaus ausgerechnet rosafarbene und lindgruene Stimmzettel ausfuellen. Auf dem rosa Stimmzettel wird die Erststimme abgegeben, auf dem gruenen die Zweitstimme. Entgegen einem weit verbreiteten Irrtum ist die Zweitstimme die wichtigere, denn sie entscheidet ueber die tatsaechliche Prozentzahl und damit ueber die Zahl der Sitze im Parlament. Mit der Erststimme werden die DirektkandidatInnen der einzelnen Wahlkreise gewaehlt, mit der Zweitstimme Bezirks- oder Landeslisten der Parteien. Ein Direktmandat erhaelt, wer die meisten Stimmen in einem Wahlkreis erringt. Die turnusmaessig wiederkehrende Verwirrung vor Wahlen beim sogenannten Wahlvolk hat sich in diesem Jahr noch dadurch gesteigert, dass die Parteien im Sommer monatelang um die Modalitaeten zur ersten gesamtdeutschen Wahl gestritten haben, an die das Berliner Wahlgesetz angelehnt wurde. Nach der Klage von PDS und den Gruenen vor dem Bundesverfassungsgericht in Karlsruhe musste das erste Wahlgesetz zurueckgezogen werden, das eine allgemeine 5-Prozent-Klausel im gesamten Wahlgebiet und das sogenannte Huckepackverfahren vorsah. Im zweiten Wahlgesetz wurde dann die sogenannte regionalisierte 5-Prozentklausel eingefuehrt. Sie bedeutet, dass zur Bundestagswahl eine Partei entweder im Gebiet der alten oder der neuen Bundeslaender die Sperrklausel ueberwinden muss, um ins Parlament einzuziehen. Im Gebiet der Ex-DDR wurden ausserdem Listenvereinigungen von mehreren Gruppierungen oder Parteien zugelassen, um die Chancen der Buergerbewegungen zu erhoehen. Auch fuer die Wahlen zum Abgeordnetenhaus gelten diese Bedingungen: Ost- und West-Berlin bilden zwar ein einheitliches Wahlgebiet, aber zwei verschiedene Zaehlgebiete. Wer es in einem der beiden Teile schafft, die Sperrklausel zu ueberwinden, sitzt automatisch im Parlament. Die Verwirrung, vor allem in der linksalternativen Waehlerschaft, ist deshalb besonders gross, weil sowohl fuer den Bundestag als auch fuer das Abgeordnetenhaus in beiden Teilen der Stadt die AL, Buendnis 90/Gruene und PDS gewaehlt werden kann. Die PDS hat sich im Sommer bundesweit mit der westlichen Linken Liste/PDS vereinigt und tritt deswegen wie die grossen Parteien CDU und SPD an. Die gruenen Parteien wollten diesen Schritt noch nicht vollziehen, haben in Berlin aber die Moeglichkeit, getrennte Landeslisten fuer das gesamte Wahlgebiet aufzustellen - was auch beide getan haben. Konkret heisst das, man kann sowohl in Schoeneberg wie in Friedrichshain mit der Zweitstimme AL und Buendnis 90 waehlen. Die Stimme ist dann nicht verloren, wenn es die beiden Parteien schaffen, in einem der beiden Zaehlgebiete ueber die Sperrklausel zu kommen - wovon trotz des unklaren Wahlausgangs auszugehen ist. Ob man mit der Erststimme einen Direktkandidaten waehlen kann, haengt davon ab, ob die entsprechende Partei im eigenen Wahlkreis ueberhaupt KandidatInnen nominiert hat. In Berlin koennen insgesamt 200 KandidatInnen in 120 Wahlkreisen gewaehlt werden. Das Berliner Abgeordnetenhaus ist damit das groesste Landesparlament der Republik, dessen Parlamentarierzahl noch durch sogenannte Ueberhangsmandate steigen wird. Ihren Sitz werden die Abgeordneten fuer eine Uebergangsfrist von etwa zwei Jahren weiter im Schoeneberger Rathaus haben. Der an sich zu kleine Plenarsaal wird bis zur konstituierenden Sitzung der neuen Regierung am 11. Januar 1991 notduerftig umgeruestet. Erstmals in der Nachkriegsgeschichte duerfen sich die BerlinerInnen auch an Wahlen zum Bundestag direkt beteiligen, d.h. jedeR darf insgesamt vier Kreuzchen machen. Fuer die Bundestagswahl gibt es einen dritten Stimmzettel, auf dem Erst- und Zweitstimme abgegeben werden. In der oeffentlichen Wahrnehmung in Berlin ist diese Wahl gegenueber den Abgeordnetenhauswahlen so gut wie untergegangen, ueberlagert vom Wahlkampf um die Sitze im Abgeordnetenhaus und die kuenftige Landesregierung. kd taz-berlin kd https://www.wiso-net.de/document/TAZH__T901201.203 Presse_1990_01_Html.htm 1990 Pressetexte/Presseartikel 1990 die
kable(head(token_text,1)) %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  scroll_box(width = "100%", height = "200px")
id header publisher author link filename year path word
1

Die Berliner waehlen in Lindgruen und Rosa

Zweitstimme ist die wichtige Stimme: Sie entscheidet ueber die tatsaechliche Prozentzahl und ueber die Zahl der Sitze im Parlament/ Erststimme bestimmt den Direktkandidaten in einem Wahlkreis/ Fuer die Bundestagswahl gibt es einen 3. Zettel
taz-berlin kd https://www.wiso-net.de/document/TAZH__T901201.203 Presse_1990_01_Html.htm 1990 Pressetexte/Presseartikel 1990 taz

Entfernen von Stopwörtern

# Liste mit deutschen Stopwörtern
stopword <- as_tibble(stopwords::stopwords("de")) 
stopword <- rename(stopword, word=value)

# Erweiterte Liste mit deutschen Stopwörtern
stopword_extented <- read_tsv("https://raw.githubusercontent.com/solariz/german_stopwords/master/german_stopwords_full.txt", col_names = FALSE, comment = ";")
stopword_extented <- rename(stopword_extented, word=X1)

# Liste eigener Stopwörtern
# bz ist das Kürzel der Badischen Zeitung
# mz ist das Kürzel der Mitteldeutsche Zeitung
# rp ist das Kürzel der Rheinischen Post
# sz ist das Kürzel der Sächsische Zeitung
# ta ist das Kürzel der Thüringer Allgemeinen
# taz ist das Kürzel der tageszeitung
# tz ist das Kürzel der tz - eine münchner zeitung
stopword_own <- tibble(word = c("bz", "mz", "rp", "sz", "tz", "ta", "taz"))

# Zusammenfügen und Entfernen von Duplikaten
all_stopword <- bind_rows(stopword,stopword_extented, stopword_own) %>% distinct()

tb_header <- anti_join(token_header, all_stopword, by = 'word')
tb_text <- anti_join(token_text, all_stopword, by = 'word')

# Visualisierung der Ergebnisse der Prozeduren
kable(head(tb_header,1)) %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  scroll_box(width = "100%", height = "200px")
id text publisher author link filename year path word
1 TAZ-Bericht Berlin. Ob das eine versteckte Koalitionsaussage ist? Zufall oder nicht, die BerlinerInnen duerfen bei den Wahlen zum Gesamtberliner Abgeordnetenhaus ausgerechnet rosafarbene und lindgruene Stimmzettel ausfuellen. Auf dem rosa Stimmzettel wird die Erststimme abgegeben, auf dem gruenen die Zweitstimme. Entgegen einem weit verbreiteten Irrtum ist die Zweitstimme die wichtigere, denn sie entscheidet ueber die tatsaechliche Prozentzahl und damit ueber die Zahl der Sitze im Parlament. Mit der Erststimme werden die DirektkandidatInnen der einzelnen Wahlkreise gewaehlt, mit der Zweitstimme Bezirks- oder Landeslisten der Parteien. Ein Direktmandat erhaelt, wer die meisten Stimmen in einem Wahlkreis erringt. Die turnusmaessig wiederkehrende Verwirrung vor Wahlen beim sogenannten Wahlvolk hat sich in diesem Jahr noch dadurch gesteigert, dass die Parteien im Sommer monatelang um die Modalitaeten zur ersten gesamtdeutschen Wahl gestritten haben, an die das Berliner Wahlgesetz angelehnt wurde. Nach der Klage von PDS und den Gruenen vor dem Bundesverfassungsgericht in Karlsruhe musste das erste Wahlgesetz zurueckgezogen werden, das eine allgemeine 5-Prozent-Klausel im gesamten Wahlgebiet und das sogenannte Huckepackverfahren vorsah. Im zweiten Wahlgesetz wurde dann die sogenannte regionalisierte 5-Prozentklausel eingefuehrt. Sie bedeutet, dass zur Bundestagswahl eine Partei entweder im Gebiet der alten oder der neuen Bundeslaender die Sperrklausel ueberwinden muss, um ins Parlament einzuziehen. Im Gebiet der Ex-DDR wurden ausserdem Listenvereinigungen von mehreren Gruppierungen oder Parteien zugelassen, um die Chancen der Buergerbewegungen zu erhoehen. Auch fuer die Wahlen zum Abgeordnetenhaus gelten diese Bedingungen: Ost- und West-Berlin bilden zwar ein einheitliches Wahlgebiet, aber zwei verschiedene Zaehlgebiete. Wer es in einem der beiden Teile schafft, die Sperrklausel zu ueberwinden, sitzt automatisch im Parlament. Die Verwirrung, vor allem in der linksalternativen Waehlerschaft, ist deshalb besonders gross, weil sowohl fuer den Bundestag als auch fuer das Abgeordnetenhaus in beiden Teilen der Stadt die AL, Buendnis 90/Gruene und PDS gewaehlt werden kann. Die PDS hat sich im Sommer bundesweit mit der westlichen Linken Liste/PDS vereinigt und tritt deswegen wie die grossen Parteien CDU und SPD an. Die gruenen Parteien wollten diesen Schritt noch nicht vollziehen, haben in Berlin aber die Moeglichkeit, getrennte Landeslisten fuer das gesamte Wahlgebiet aufzustellen - was auch beide getan haben. Konkret heisst das, man kann sowohl in Schoeneberg wie in Friedrichshain mit der Zweitstimme AL und Buendnis 90 waehlen. Die Stimme ist dann nicht verloren, wenn es die beiden Parteien schaffen, in einem der beiden Zaehlgebiete ueber die Sperrklausel zu kommen - wovon trotz des unklaren Wahlausgangs auszugehen ist. Ob man mit der Erststimme einen Direktkandidaten waehlen kann, haengt davon ab, ob die entsprechende Partei im eigenen Wahlkreis ueberhaupt KandidatInnen nominiert hat. In Berlin koennen insgesamt 200 KandidatInnen in 120 Wahlkreisen gewaehlt werden. Das Berliner Abgeordnetenhaus ist damit das groesste Landesparlament der Republik, dessen Parlamentarierzahl noch durch sogenannte Ueberhangsmandate steigen wird. Ihren Sitz werden die Abgeordneten fuer eine Uebergangsfrist von etwa zwei Jahren weiter im Schoeneberger Rathaus haben. Der an sich zu kleine Plenarsaal wird bis zur konstituierenden Sitzung der neuen Regierung am 11. Januar 1991 notduerftig umgeruestet. Erstmals in der Nachkriegsgeschichte duerfen sich die BerlinerInnen auch an Wahlen zum Bundestag direkt beteiligen, d.h. jedeR darf insgesamt vier Kreuzchen machen. Fuer die Bundestagswahl gibt es einen dritten Stimmzettel, auf dem Erst- und Zweitstimme abgegeben werden. In der oeffentlichen Wahrnehmung in Berlin ist diese Wahl gegenueber den Abgeordnetenhauswahlen so gut wie untergegangen, ueberlagert vom Wahlkampf um die Sitze im Abgeordnetenhaus und die kuenftige Landesregierung. kd taz-berlin kd https://www.wiso-net.de/document/TAZH__T901201.203 Presse_1990_01_Html.htm 1990 Pressetexte/Presseartikel 1990 berliner
kable(head(tb_text,1)) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F) %>%
  scroll_box(width = "100%", height = "200px")
id header publisher author link filename year path word
1

Die Berliner waehlen in Lindgruen und Rosa

Zweitstimme ist die wichtige Stimme: Sie entscheidet ueber die tatsaechliche Prozentzahl und ueber die Zahl der Sitze im Parlament/ Erststimme bestimmt den Direktkandidaten in einem Wahlkreis/ Fuer die Bundestagswahl gibt es einen 3. Zettel
taz-berlin kd https://www.wiso-net.de/document/TAZH__T901201.203 Presse_1990_01_Html.htm 1990 Pressetexte/Presseartikel 1990 bericht

Stemming - Vergleich der Wörter in den Artikeltexten

tb_text_stem <- tb_text %>% 
  mutate(word_stem = wordStem(.$word, language = "german")) %>% 
  dplyr::count(word_stem, sort = TRUE)

graph1 <- tb_text_stem %>% 
  top_n(10) %>% 
  ggplot() +
  aes(x = reorder(word_stem, n), y = n) +
  geom_col() + 
  labs(title = "mit Stemming") +
  coord_flip() +
  theme(axis.title.y=element_blank())

graph2 <- tb_text %>% 
  dplyr::count(word, sort = TRUE) %>% 
  top_n(10) %>% 
  ggplot() +
  aes(x = reorder(word, n), y = n) +
  geom_col() +
  labs(title = "ohne Stemming") +
  coord_flip() +
  theme(axis.title.y=element_blank())

grid.arrange(graph1,graph2, ncol=2, nrow=1,
     top = textGrob("Wörter in den Artikeltexten",gp=gpar(fontsize=18)))

Interpretation

Wenig überraschend tauchen die Namen der Parteien und Begriffen wie ‘bundestagswahl’, ‘wahl’ oder ‘prozent’ eine herausragende Rolle. Zwischen einer Analyse mit oder ohne Stemming besteht kein wesentlicher Unterschied.

Stemming - Vergleich der Wörter in den Artikelüberschriften

tb_header_stem <- tb_header %>% 
  mutate(word_stem = wordStem(.$word, language = "german")) %>% 
  dplyr::count(word_stem, sort = TRUE)

graph1 <- tb_header_stem %>% 
  top_n(10) %>% 
  ggplot() +
  aes(x = reorder(word_stem, n), y = n) +
  geom_col() + 
  labs(title = "mit Stemming") +
  coord_flip() +
  theme(axis.title.y=element_blank())

graph2 <- tb_header %>% 
  dplyr::count(word, sort = TRUE) %>% 
  top_n(10) %>% 
  ggplot() +
  aes(x = reorder(word, n), y = n) +
  geom_col() +
  labs(title = "ohne Stemming") +
  coord_flip() +
  theme(axis.title.y=element_blank())

grid.arrange(graph1,graph2, ncol=2, nrow=1,
     top = textGrob("Wörter in den Artikelüberschriften",gp=gpar(fontsize=18)))

Interpretation

Die Situation bei den Wörtern in den Überschriften unterscheidet sich nicht wesentlich. Insgesamt aber scheint die Aufbereitung mit Stemming leicht stabiler zu sein. Deshalb werden in den nachfolgenden Kapiteln ausschließlich gestemmte Wörter benutzt.

# Überschreiben der Dataframes mit den gestemmten Daten 
tb_text_stem <- tb_text %>% 
  mutate(word_stem = wordStem(.$word, language = "german"))

tb_header_stem <- tb_header %>% 
  mutate(word_stem = wordStem(.$word, language = "german"))

7.3. Explorative Datenanalyse

Worthäufigkeiten (tf - term frequency)

word_count_header <- count(tb_header, word, sort = TRUE)
word_count_text <- count(tb_text, word, sort = TRUE)

word_count_header %>% 
  top_n(10) %>% 
  ggplot() +
  aes(x = reorder(word, n), y = n) +
  geom_col() + 
  labs(title = "Top 10 Wörter in den Artikelüberschriften") +
  coord_flip() +
  theme(axis.title.y=element_blank())

word_count_text %>% 
  top_n(10) %>% 
  ggplot() +
  aes(x = reorder(word, n), y = n) +
  geom_col() + 
  labs(title = "Top 10 Wörter in den Artikeltexten") +
  coord_flip() +
  theme(axis.title.y=element_blank())

Worthäufigkeiten nach Publikation

# Anzahl der Artikel pro Publikation
publisher_articles_count <- count(tb_text, publisher, sort = TRUE)
# Anzahl der Token der Artikeltexte pro Publikation
publisher_articles_word_text_count <-  count(tb_text, publisher, word, sort = TRUE)
# Anzahl der Token in den Artikelüberschriften pro Publikation
publisher_articles_word_header_count <-  count(tb_header, publisher, word, sort = TRUE)

# Artikel pro Publikation
kable(head(publisher_articles_count[order(-publisher_articles_count$n),])) %>%  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F)
publisher n
mitteldeutsche zeitung 164548
welt online 138942
zeit online 115527
der tagesspiegel 102536
badische zeitung 96158
rheinische post 85634
# Top Token aus den Artikeltexten pro Publikation
kable(head(publisher_articles_word_text_count[order(-publisher_articles_word_text_count$n),])) %>%  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F)
publisher word n
mitteldeutsche zeitung 3993
mitteldeutsche zeitung r 3791
welt online 3393
welt online r 3067
zeit online 2600
zeit online r 2262
# Top Token aus den Artikelüberschriften pro Publikation
kable(head(publisher_articles_word_header_count[order(-publisher_articles_word_header_count$n),])) %>%  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F)
publisher word n
rheinische post bundestagswahl 533
badische zeitung bundestagswahl 391
sã¤chsische zeitung bundestagswahl 355
thã¼ringer allgemeine bundestagswahl 308
mitteldeutsche zeitung bundestagswahl 261
sã¤chsische zeitung thema 214

Visualisierung der Worthäufigkeiten nach Parteien

article_patei_cdu_count <- tb_text %>% filter(word == "cdu"|word == "csu") %>% group_by(year)  %>% count(year, sort = TRUE) %>% ungroup() 
article_patei_cdu_count$word <- "cdu"

article_patei_spd_count <- tb_text %>% filter(word == "spd") %>% group_by(year)  %>% count(word, sort = TRUE) %>% ungroup()

article_patei_gruene_count <- tb_text %>% filter(word == "gruenen"|word == "b90") %>% group_by(year)  %>% count(year, sort = TRUE) %>% ungroup()
article_patei_gruene_count$word <- "gruene"

article_patei_fdp_count <- tb_text %>% filter(word == "fdp") %>% group_by(year)  %>% count(word, sort = TRUE) %>% ungroup()

article_patei_afd_count <- tb_text %>% filter(word == "afd") %>% group_by(year)  %>% count(word, sort = TRUE) %>% ungroup()

article_patei_linke_count <- tb_text %>% filter(word == "linken" | word == "pds") %>% group_by(year)  %>% count(year, sort = TRUE) %>% ungroup()
article_patei_linke_count$word <- "linke"

article_patei_total <- rbind(article_patei_cdu_count,
                             article_patei_spd_count,
                             article_patei_fdp_count,
                             article_patei_gruene_count,
                             article_patei_linke_count,
                             article_patei_afd_count)

article_patei_percent <- group_by(article_patei_total, year) %>% 
                         mutate(percent = n/sum(n) * 100) %>%
                         ungroup()

pateifarben <- c("blue", "black", "yellow", "green", "purple", "red")

ggplot(data=article_patei_percent, aes(x=year, y=percent, fill=word )) +
  geom_bar(stat="identity",
           position='stack')+
  scale_fill_manual(values=pateifarben) +
  xlab("Nennungen in der Presse") +
  ylab("Prozent") +
  theme_minimal() +
  theme(legend.title = element_blank()) +
  ggtitle("Pressepräsenz der Parteien im Vergleich")

Interpretation

Die Visualisierung verdeutlicht die Präsenz der verschiedenen Parteien in der Presse im Vorfeld der Bundestagswahlen. Über den Betrachtungszeitraum schwankt die Pressepräsenz der Parteien zum Teil deutlich. Besonders deutlich wird das in den frühen 2000 Jahren bei der Linkspartei oder in der jüngsten Vergangenheit bei der AFD. Insgesamt lässt sich beobachten, dass kleinere Parteien wie FDP oder Grüne in der Presse recht gut vertreten sind - zumindest wenn man die Pressepräsenz mit dem prozentualen Abschneiden bei der Wahl vergleicht. Ebenfalls interessant ist zu beobachten, dass die Regierungsparteien etwas stärker in der Presse präsent sind als die Opposition - beispielsweise die Union in den 1990er Jahren oder die SDP während der Regierungszeit Gehard Schröders.

Vergleich der Pressepräsenz der Parteien mit den Wahlergebnissen

# Funktion, um die Wahlergebnisse der jeweiligen Partei einzulesen
read_wahlergebnis <- function(partei) {
  z <- read_csv2("https://raw.githubusercontent.com/TheFakeStefan/DataSciencewithR/master/data/bundestagswahlergebnisse_1990_2017.csv", 
                 col_type = cols()) %>% 
  select(Wahljahr,toupper(partei)) %>% 
  as.data.frame() %>%
  rename("year" = Wahljahr, "percent" = toupper(partei))
  z$type <- "r"
  z$word <- tolower(partei)
  z$n <- 0
  return(z)
}

# Anwenden der Funktion
wahlergbnis_cdu <- read_wahlergebnis("CDU")
wahlergbnis_spd <- read_wahlergebnis("SPD")
wahlergbnis_linke <- read_wahlergebnis("LINKE")
wahlergbnis_gruene <- read_wahlergebnis("GRUENE")
wahlergbnis_afd <- read_wahlergebnis("AFD")
wahlergbnis_sonstige <- read_wahlergebnis("SONSTIGE")
wahlergbnis_fdp <- read_wahlergebnis("FDP")

# Zusammenführen der Daten
wahlergbnis_patei_percent <- rbind(wahlergbnis_cdu,
                             wahlergbnis_spd,
                             wahlergbnis_fdp,
                             wahlergbnis_gruene,
                             wahlergbnis_linke,
                             wahlergbnis_afd,
                             wahlergbnis_sonstige)

# Zusammenführen mit den Pressedaten
article_patei_percent$type <- "p"
graphdata <- rbind(article_patei_percent,wahlergbnis_patei_percent)

# Vorbereitende Formatierungsarbeiten
graphdata$word <- toupper(graphdata$word)

# Richtige Farbkennung der Parteien
pateifarben <- c("blue", "black", "yellow", "green", "purple", "grey", "red")

# Plotten der Grafik
ggplot(data=graphdata, aes(x=type, y=percent, fill=word )) +
  geom_bar(stat="identity",
           position='stack')+
  facet_grid( ~ year) +
  scale_fill_manual(values=pateifarben) +
  xlab("(P)resse VS (R)ealität") +
  ylab("Prozent") +
  labs(caption = "... auf Basis der Nennungen in den Artikeltexten.") +
  theme_minimal() +
  theme(legend.title = element_blank()) +
  ggtitle("Vergleich der Wahlergebnisse mit der Relevanz in der Presse")

Interpretation

Das oben Gesagte bestätigt sich zum Teil bei dieser direkten Gegenüberstellung von Pressepräsenz und Wahlergebnissen. Auffällig ist die Situation der Volksparteien. Die Union schnitt prozentual bei den letzten acht Bundestagswahlen besser ab als als es ihre relative Pressepräsenz vermuten ließe. Bei der SPD ist die Situation gemischt. Bis 2002 waren ihre Wahlergebnisse besser als ihre Pressepräsenz, seither ist es umgekehrt. Die FDP, die Linkspartei und zum Teil auch die Grünen zeichnen sich dadurch aus, dass ihre Pressepräsenz stärker ausgeprägt ist als ihr tatsächlicher Wahlerfolg.

Anzahl der Artikel pro Publikation

publisher_articles_count <- count(tb_text, publisher, sort =T) %>% top_n(10)

xlim <- c(0, 1.1*max(publisher_articles_count$n))
par(mar=c(5,8,4,1)+.1)
xx <- barplot(publisher_articles_count$n, 
              main = "Anzahl der Artikel nach Verlagen", 
              xlab = "Anzahl der Artikel",
              horiz = T,
              col = "lightblue")
# Text in den Balken des Balkendiagramms
text(y= xx, x = publisher_articles_count$n, label = publisher_articles_count$n, pos = 2, cex = 1.0, col = "black")
# y-Achse und Labels
axis(side=2, at=xx, labels = publisher_articles_count$publisher, las=1, cex.axis=0.7)

Interpretation

Bei der Betrachtung des Balkendiagramms fällt auf, dass die Mitteldeutsche Zeitung mit einer sehr großen Anzahl von Beiträgen vertreten ist. Dieses könnte auch daran liegen, dass die Publikation seit langem in der WISO-Datenbank vertreten ist und vermutliche zahlreiche lokale Unterausgaben als einzelne Artikel gerechnet werden.

Durchschnittliche Wortzahl pro Artikel und Publikation

publisher_articles_count <- count(tb_text, publisher, sort =T) %>% top_n(10) %>% 
  rename(total_articles = n)

wortanzahl_article <- tb_text %>% group_by(publisher,id) %>% tally(name = "wortanzahl") 
wortanzahl_article_pro_publikation <- 
  aggregate(wortanzahl_article$wortanzahl, by=list(wortanzahl_article$publisher), mean) %>% 
  rename(publisher = Group.1,mean_wortanzahl = x)

publisher_articles_count_mean <- merge(x=publisher_articles_count,y=wortanzahl_article_pro_publikation,by="publisher")  

publisher_articles_count_mean_plot <- barplot(as.matrix(publisher_articles_count_mean$mean_wortanzahl), 
              main = "Anzahl der durchschnittlichen Wörter in einem Artikel pro Publikation", 
              xlab = "Anzahl der Wörter",
              horiz = T,
              col = "lightblue",
              beside=TRUE)
# y-Achse und Labels
axis(side=2, at=publisher_articles_count_mean_plot, labels = publisher_articles_count$publisher, las=1, cex.axis=0.7)

Interpretation

Augenfällig ist in dieser Darstellung, dass die Artikel von Online-Publikationen im Durchschnitt deutlich kürzer sind als Artikel aus Printmedien.

Worthäufigkeiten in den Artikeltexten

articles_word_text_count <- tb_text %>% count(word)
articles_word_text_count_n1 <- tb_text %>% count(word) %>% filter(n == 1) %>% summarise("Gesamtanzahl der Token, welche 1x vorkommen" = sum(n))
kable(articles_word_text_count_n1)
Gesamtanzahl der Token, welche 1x vorkommen
19973
hist(articles_word_text_count$n,
breaks=40,
main="Verteilung der Token in den Artikeltexten",
xlab="Häufigkeit der Tokens",
ylab="Verteilung",
col="blue",
)

Interpretation

Es ist deutlich zu erkennen, dass circa 42.500 Token lediglich einmal vorkommen. Daher werden im Nachfolgenden alle Token herausgefiltert, welche nicht mindestens 50x vorkommen.

articles_word_text_count_without_n50 <- 
  publisher_articles_word_text_count %>%
  filter(n > 49 )

hist(articles_word_text_count_without_n50$n,
breaks=40,
main="Verteilung der Token (n > 49) in den Artikeltexten",
sub="42,5k Token kommen weniger 50x vor",
xlab="Häufigkeit der Tokens",
ylab="Verteilung",
col="blue",
)

Interpretation

1.000 Token kommen mehr als 50x in den Artikeltexten vor. Auch hier zeigt sich eine rechtsschiefe Verteilung, allerdings bei Weitem nicht so extrem ausgeprägt wie vor dem Herausfiltern von Wörtern mit weniger als 50 Nennungen.

Worthäufigkeiten in den Artikelüberschriften ohne Stoppwörter

articles_word_header_count <- tb_header %>% count(word)
articles_word_header_count_n1 <- tb_header %>% count(word) %>% filter(n == 1) %>% summarise("Gesamtanzahl der Token, welche 1x vorkommen" = sum(n))

kable(articles_word_header_count_n1)
Gesamtanzahl der Token, welche 1x vorkommen
3021
hist(articles_word_header_count$n,
breaks=40,
main="Verteilung der Token in den Artikelüberschriften",
xlab="Häufigkeit der Tokens",
ylab="Verteilung",
col="blue",
)

Interpretation

Es ist deutlich zu erkennen, dass circa 4.700 Token in den Artikelüberschriften lediglich einmal vorkommen. Daher werden im Nachfolgenden alle Token herausgefiltert, welche nicht mindestens 10x vorkommen.

articles_word_header_count_n10 <- 
  articles_word_header_count %>%
  filter(n > 9 )

hist(articles_word_header_count_n10$n,
breaks=40,
main="Verteilung der Token (n > 9) in den Artikelüberschriften",
xlab="Häufigkeit der Tokens",
ylab="Verteilung",
col="blue",
)

Interpretation

Übrig bleiben 369 Token in den Artikelüberschriften, welche mehr als 10x vorgekamen. Auch hier zeigt sich eine ausgeprägt rechtsschiefe Verteilung.

Worthäufigkeiten in den Artikeltexten nach Wahljahren

# Anzahl der Artikel nach Jahren
year_articles_count <- count(tb_text, year, sort = TRUE)
# Anzahl der Token in den Artikeltexten nach Jahren
year_articles_word_text_count <-  count(tb_text, year, word, sort = TRUE)
# Anzahl der Token in den Artikelüberschriften nach Jahren
year_articles_word_header_count <-  count(tb_header, year, word, sort = TRUE)

# Artikel nach Jahren
kable(head(year_articles_count[order(-year_articles_count$n),])) %>%  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F)
year n
2017 706818
2013 345166
2005 162863
2009 138801
2002 59804
1998 52435
# Top Token in den Artikeltexten nach Jahren
kable(head(year_articles_word_text_count[order(-year_articles_word_text_count$n),])) %>%  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F)
year word n
2017 16212
2017 r 15038
2017 ã 8316
2013 7636
2017 6898
2013 r 6874
# Top Token in den Artikelüberschriften nach Jahren
kable(head(year_articles_word_header_count[order(-year_articles_word_header_count$n),])) %>%  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F)
year word n
2005 bundestagswahl 859
2017 bundestagswahl 850
2013 bundestagswahl 820
2009 bundestagswahl 788
2017 282
2005 277
Interpretation

Die Visualisierung zeigt deutlich, dass die Anzahl der Artikel über den Betrachtungszeitraum deutlich zugenommen hat. Dieses ist jedoch eher auf den Ausbau der WISO-Datenbank über die Jahre zurückzuführen als auf den tatsächlichen Umfang der Berichterstattung.

Analyse von Themen nach Wahljahren

In diesem Abschnitt sollen relevante Themen der Wahljahre identifiziert werden. Dazu werden spezielle Stopwörter verwendet, um Parteien und häufige themenirrelevante Begriffe zu entfernen.

themen_der_wahlen <- tb_text %>% 
  filter(!word %in% c("afd","berlin","bonn","bundestag","bundestagswahl","buendnis","cdu", "csu",
                      "dezember","fdp", "gruene","gruenen","jahren","kandidaten","linke","oktober",
                      "partei","parteien","pds","prozent", "september","spd","thema", "uhr","union",
                      "wahl","wahlkreis","waehlen","waehler") &
           !str_detect(word, pattern = "[[:digit:]]"))

themen_der_wahlen_top5_count <- 
  themen_der_wahlen %>% 
  count(word, year) %>%
  group_by(year) %>%
  top_n(n=5)

themen_der_wahlen_top5_count %>% 
  ggplot(aes(word, n, fill = as.factor(year))) +
  scale_y_continuous(expand = c(0, 0)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "n") +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Wahljahr", 
       title = "Top5 Token der Wahljahre", 
       subtitle="in den Artikeltexten",
       x= NULL, 
       y= "Häufigkeit") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set1") +
  facet_wrap(~year,scales='free', ncol=2)

Interpretation

Die Aufstellung der Top5 Begriffe verdeutlich zum einen, welche der handelnden Personen bei der jeweiligen Wahl im Zentrum des Interesses standen. Vereinzelt kann man auch Hinweise auf dominierende Themen erkennen, so zum Beispiel im Jahre 1990 die noch frische Erinnerung an die DDR, im Jahr 2005 das Thema Arbeit (Agenda 2010 und Hartz-Reformen), sowie im Jahr 2002 das Aufkommen der Schill-Partei in Hamburg.

Top10 bigrams in den Artikelüberschriften im Jahr 2017

bigram_header_2017 <- presse_cleaned %>%
  filter(year == "2017")  %>% 
  unnest_tokens(bigram, header, token = "ngrams", n = 2)

bigram_header_2017 %>%
  count(bigram, sort = TRUE) %>%
  top_n(10) %>%
  mutate(bigram = reorder(bigram, n)) %>%
  ggplot(aes(bigram, n)) +
  geom_col() +
  xlab(NULL) +
  ylab("Häufigkeit (n)") + 
  coord_flip() +
  theme_minimal()

Interpretation

Ohne den Ausschluss von Stopwords ist diese Analyse nicht sonderlich aussagekräftig.

Top5 bigrams in den Artikeltexten der Badischen Zeitung und der Zeit Online

bigram_texts_zeit_online_2017 <- presse_cleaned %>%
  filter(publisher == "badische zeitung" | publisher == "zeit online") %>%
  unnest_tokens(bigram, header, token = "ngrams", n = 2)

bigram_texts_zeit_online_2017 %>%
  count(bigram, sort = TRUE) %>%
  top_n(10) %>%
  mutate(bigram = reorder(bigram, n)) %>%
  ggplot(aes(bigram, n)) +
  geom_col() +
  xlab(NULL) +
  ylab("Häufigkeit (n)") + 
  labs(title="Top10 Token für die Wahljahre", 
       subtitle="aus dem Jahr 2017 der Verlage Badische Zeitung und Zeit Online") +
  coord_flip() +
  theme_classic()

Interpretation

Die Visualisierung zeigt leider nur häufig verwendete Artikel, Präpositionen, Bindewörter und andere Wortkombinationen, welche zu großen Teilen Stopwörter der deutschen Sprache sind. Die Stopwörter werden erst nach dem Zerlegen der Token angewendet. In diesem Fall müssen die bigrams genauer betrachtet werden und Stopwörter von diesen entfernt werden.

bigram_texts_zeit_online_2017_separated <- bigram_texts_zeit_online_2017 %>%
  separate(bigram, c("word1", "word2"), sep = " ")

bigram_texts_zeit_online_2017_filtered <- bigram_texts_zeit_online_2017_separated %>%
  filter(!word1 %in% all_stopword$word) %>%
  filter(!word2 %in% all_stopword$word) %>%
  filter(!is.na(word1))

bigram_texts_zeit_online_2017_counts <- bigram_texts_zeit_online_2017_filtered %>% 
  count(word1, word2, sort = TRUE)

bigram_texts_zeit_online_2017_united <- bigram_texts_zeit_online_2017_filtered %>%
  unite(bigram, word1, word2, sep = " ")

bigram_texts_zeit_online_2017_united %>%
  count(bigram, sort = TRUE) %>%
  top_n(9) %>%
  mutate(bigram = reorder(bigram, n)) %>%
  ggplot(aes(bigram, n)) +
  geom_col() +
  xlab(NULL) +
  ylab("Häufigkeit (n)") + 
  coord_flip() +
  theme_minimal()

Interpretation

Die Darstellung ist wesentlich besser, jedoch sind vermehrt die Daten der vergangenen Wahltage als bigrams zu sehen. Daher werden im Nachfolgenden auch die Zahlen von den bigrams entfernt.

bigram_texts_zeit_online_2017_separated <- bigram_texts_zeit_online_2017 %>%
  separate(bigram, c("word1", "word2"), sep = " ")

bigram_texts_zeit_online_2017_filtered <- bigram_texts_zeit_online_2017_separated %>%
  filter(!str_detect(word1, pattern = "[[:digit:]]")) %>%
  filter(!str_detect(word2, pattern = "[[:digit:]]")) %>%
  filter(!is.na(word1))

bigram_texts_zeit_online_2017_counts <- bigram_texts_zeit_online_2017_filtered %>% 
  count(word1, word2, sort = TRUE)

bigram_texts_zeit_online_2017_united <- bigram_texts_zeit_online_2017_filtered %>%
  unite(bigram, word1, word2, sep = " ")

bigram_texts_zeit_online_2017_united %>%
  count(bigram, sort = TRUE) %>%
  top_n(9) %>%
  mutate(bigram = reorder(bigram, n)) %>%
  ggplot(aes(bigram, n)) +
  geom_col() +
  xlab(NULL) +
  ylab("Häufigkeit (n)") + 
  coord_flip() +
  theme_minimal()

Interpretation

Nach diesem Bearbeitungsschritt zeigen sich die Namen einiger Spitzenpolitiker sowie von regionaler Wahlkreiskandidaten.

Top10 Wörter der Publikationen in den Artikeltexten nach Wahljahren

top10_words_text_by_years <- tb_text %>% 
  select(year,word) %>% 
  filter(!str_detect(word, pattern = "[[:digit:]]")) %>%
  count(year, word, sort = TRUE) %>% 
  group_by_(~ year) %>% 
  top_n(10)

top10_words_text_by_years %>%
  ggplot(aes(word, n)) +
  geom_col(aes(fill=as.factor(year))) +
  xlab(NULL) +
  scale_y_continuous(expand = c(0, 0)) +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Nach Jahr", title="Worthäufigkeiten in Artikeltexten nach Jahren", subtitle="Top10 Token für die Wahljahre") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set1")

Interpretation

Über den gesamten Betrachtungszeitraum hinweg zeigt sich, dass die SPD das größte Volumen an Presseartikeln auf sich vereinigen konnte. Gefolgt wird die SPD von der CDU und mit etwas Abstand der FDP und den Grünen. Auffallend ist, dass im Jahr 2017 die AFD fast auf ein ebenso großes Artikelvolumen kam wie die CDU. Die CSU zeigt ein äußerst geringes Volumen an Berichten, was vielleicht auf darauf zurückgeführt werden kann, dass große bayerische Zeitungen (beispielsweise die Süddeutsche Zeitung) nicht in der WISO-Datenbank vertreten sind.

Worthäufigkeiten der Publikationen in den Artikelüberschriften nach Wahljahren

top10_words_header_by_years <- tb_text %>% 
  select(year,word) %>% 
  filter(!str_detect(word, pattern = "[[:digit:]]")) %>%
  count(year, word, sort = TRUE) %>% 
  group_by_(~ year) %>% 
  top_n(10)

top10_words_header_by_years %>%
  ggplot(aes(word, n)) +
  geom_col(aes(fill=as.factor(year))) +
  xlab(NULL) +
  scale_y_continuous(expand = c(0, 0)) +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Nach Jahr", title="Worthäufigkeiten in Artikeltexten nach Jahren", subtitle="Top10 Token für die Wahljahre") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set1")

Interpretation

Bei der Auswertung der Einzelnennungen zeigt sich ein recht ähnliches Bild wie bei der Analyse des Artikelvolumens.

Top10 Token in den Artikeltexten der Top10 Publikationen (nach Anzahl der Artikel)

# Filtern der Top10 Zeitungen
publisher_top10_text <- tb_text %>%
  add_count(publisher) %>% 
  filter(!str_detect(word, pattern = "[[:digit:]]")) %>%
  filter(dense_rank(-n) <= 10)

publisher_top10_text_count <- 
  publisher_top10_text %>% 
  count(publisher)

publisher_top10_text %>%
  count(publisher, word, sort = TRUE) %>%
  group_by(publisher) %>%
  top_n(10) %>%
  ggplot(aes(x = word, y = n)) +
  geom_col(aes(fill=publisher)) +
  xlab(NULL) +
  scale_y_continuous(expand = c(0, 0)) +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Nach Verlag", title="Worthäufigkeiten", subtitle="Top10 Token in den Artikeltexten der Top10 Verlage (nach Anzahl der Artikel)") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set3")

Top5 Token in den Artikelüberschriften der Top10 Verlage (nach Anzahl der Artikel)

# Filtern der Top10 Zeitungen
publisher_top5_head <- tb_header %>% add_count(publisher) %>% filter(dense_rank(-n) <= 5)
publisher_top5_head_count <- publisher_top5_head %>% count(publisher)

publisher_top5_head %>%
  count(publisher, word, sort = TRUE) %>%
  group_by(publisher) %>%
  top_n(3) %>%
  ggplot(aes(x = word, y = n)) +
  geom_col(aes(fill=publisher)) +
  xlab(NULL) +
  scale_y_continuous(expand = c(0, 0)) +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Nach Verlag", title="Worthäufigkeiten", subtitle="Top10 Token in den Artikelüberschriften der Top10 Verlage (nach Anzahl der Artikel)") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set3")


7.3. Wordclouds

Wordcloud mit den 15 häufigsten Wörtern aller Artikeltexte über alle Jahre

library(wordcloud)

tb_text %>%
  count(word) %>%
  with(wordcloud(word, n, max.words = 15))

Wordcloud2 mit den Top200 Wörtern aller Artikeltexten über alle Jahre

library(wordcloud2)

top200_text_wordcloud <- tb_text %>% 
  count(word) %>% 
  top_n(200)

# Durch einen Fehler im Paket wordcloud2 werden die Grafiken nicht in das HTML-Dokument durch den Knit-Prozess übertragen.
# Um die HTML Datei trotzdem mit der Grafik zu erzeugen, werden die nachfolgenden Zeilen Code zusätzlich verwendet, um diesen
# Fehler im Paket zu umgehen. Ein mögliches Update des Pakets soll das Problem in der Zukunft lösen. Sofern das Update den Fehler behebt, kann die
# nachfolgende auskommentierte Zeile die darauffolgenden im Codechunk ersetzen.
#wordcloud2(top200_text_wordcloud)
graph_wordcloud2_1 <- wordcloud2(top200_text_wordcloud)
saveWidget(graph_wordcloud2_1, "graph_wordcloud2_1.html", selfcontained = F)
webshot("graph_wordcloud2_1.html", "graph_wordcloud2_1.png", delay = 10, vwidth = 800, vheight = 800)

Interpretation

Wie bereits bei der Analyse der Worthäufigkeiten, zeigt sich auch in der Darstellung der Wordcloud eine Mischung aus Parteiennamen, Namen von handelnden Personen, wahlspezifischen Begriffen sowie vereinzelt politischen Themen. Die Daten der Wahltage tauchen ebenfalls auf, könnten aber durch eine Stopword-Behandlung eliminiert werden.

Wordcloud2 mit den Top100 Wörtern aller Artikeltexte des Jahres 2013

# Alle Wörter des Jahres 2013
top100_text_wordcloud_2013 <- tb_text %>% 
  filter(year == 2013) %>%
  count(word) %>% 
  top_n(100)

# Durch einen Fehler im Paket wordcloud2 werden die Grafiken nicht in das HTML-Dokument durch den Knit-Prozess übertragen.
# Um die HTML Datei trotzdem mit der Grafik zu erzeugen, werden die nachfolgenden Zeilen Code zusätzlich verwendet, um diesen
# Fehler im Paket zu umgehen. Ein mögliches Update des Paket soll das Problem in der Zukunft lösen. Sofern das Update den Fehler behebt, kann die
# nachfolgende auskommentierte Zeile die darauffolgenden im Codechunk ersetzen. 
#wordcloud2(top100_text_wordcloud_2013)
graph_wordcloud2_4 <- wordcloud2(top100_text_wordcloud_2013)
saveWidget(graph_wordcloud2_4, "graph_wordcloud2_4.html", selfcontained = F)
webshot("graph_wordcloud2_4.html", "graph_wordcloud2_4.png", delay = 10, vwidth = 800, vheight = 800)

Wortcloud2 mit den Top100 Wörtern aller Artikeltexte des Jahres 2017

Im Vergleich der beiden Jahre 2013 und 2017 wird die deutliche Fokussierung auf die AFD im Jahr 2017 deutlich.

# Alle Wörter des Jahres 2017
top100_text_wordcloud_2017 <- tb_text %>% 
  filter(year == 2017) %>%
  count(word) %>% 
  top_n(100)

# Durch einen Fehler im Paket wordcloud2 werden die Grafiken nicht in das HTML-Dokument durch den Knit-Prozess übertragen.
# Um die HTML Datei trotzdem mit der Grafik zu erzeugen, werden die nachfolgenden Zeilen Code zusätzlich verwendet, um diesen
# Fehler im Paket zu umgehen. Ein mögliches Update des Pakets soll das Problem in der Zukunft lösen. Sofern das Update den Fehler behebt, kann die
# nachfolgende auskommentierte Zeile die darauffolgenden im Codechunk ersetzen.
#wordcloud2(top100_text_wordcloud_2017)
graph_wordcloud2_5 <- wordcloud2(top100_text_wordcloud_2017)
saveWidget(graph_wordcloud2_5, "graph_wordcloud2_5.html", selfcontained = F)
webshot("graph_wordcloud2_5.html", "graph_wordcloud2_5.png", delay = 10, vwidth = 800, vheight = 800)

Interpretation

Im direkten Vergleich der Jahre 2013 und 2017 zeigt sich deutlich das gestiegene Interesse der Presse an der AFD. Die SPD steht 2017 bei weitem nicht so sehr im Vordergrund wie in der Berichterstattung des Jahres 2013. Davon profitierte der SPD Spitzenkandidat des Jahres 2017 - Martin Schulz - der eine weit größere Pressepräsenz zeigte als sein Vorgänger Steinbrück im Jahre 2013.

Wordcloud2 mit den Top100 Wörtern des Tagesspiegel

# Alle Wörter des Jahres 2017
top100_text_der_tagesspiegel_wordcloud <- tb_text %>% 
  filter(publisher == "der tagesspiegel") %>%
  count(word) %>% 
  top_n(100)

# Durch einen Fehler im Paket wordcloud2 werden die Grafiken nicht in das HTML-Dokument durch den Knit-Prozess übertragen.
# Um die HTML Datei trotzdem mit der Grafik zu erzeugen, werden die nachfolgenden Zeilen Code zusätzlich verwendet, um diesen
# Fehler im Paket zu umgehen. Ein mögliches Update des Paket soll das Problem in der Zukunft lösen. Sofern das Update den Fehler behebt, kann die
# nachfolgende auskommentierte Zeile die darauffolgenden im Codechunk ersetzen.
#wordcloud2(top100_text_der_tagesspiegel_wordcloud)
graph_wordcloud2_6 <- wordcloud2(top100_text_der_tagesspiegel_wordcloud)
saveWidget(graph_wordcloud2_6, "graph_wordcloud2_6.html", selfcontained = F)
webshot("graph_wordcloud2_6.html", "graph_wordcloud2_6.png", delay = 10, vwidth = 800, vheight = 800)

Interpretation

Spontan fällt auf, dass der Tagesspiegel scheinbar besonders gerne über die SPD und kaum über die AFD berichtet.


7.4. TF-IDF Analysen

Top5 Wörter in den Artikeltexten der Top6 Publikationen sortiert nach TF-IDF

# Filtern der Top6 Zeitungen
publisher_top6 <- tb_text %>% add_count(publisher) %>% filter(dense_rank(-n) <= 6)
publisher_top6_count <- publisher_top6 %>% count(publisher)

publisher_top6_plot <- publisher_top6 %>%
  count(publisher, word, sort = TRUE) %>%
  bind_tf_idf(word, publisher, n) %>%
  mutate(word = fct_reorder(word, tf_idf))

publisher_top6_plot %>% 
  group_by(publisher) %>% 
  top_n(5, tf_idf) %>% 
  ungroup() %>%
  mutate(word = reorder(word, tf_idf)) %>%
  ggplot(aes(word, tf_idf, fill = publisher)) +
  scale_y_continuous(expand = c(0, 0)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "tf-idf") +
  facet_wrap(~publisher, ncol = 2, scales = "free") +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Author", 
       title = "Häufigkeit nach TF-IDF (inverse Dokumenthäufigkeit)", 
       subtitle="Top5 Wörter in den Artikeltexten der Top6 Verlage mit den meisten Artikeln",
       x= NULL, 
       y= "tf-idf") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set3")  

Interpretation

Eine TF-IDF Analyse macht bei einer Analyse dieser Art nicht sonderlich viel Sinn. Hintergrund: Die Vorauswahl der Presseberichte geschah entlang der Thematik ‘Bundestagswahl’. Das bedeutet, dass der gesamte Textcorpus sich mit dem Thema ‘Wahlen’ auseinandersetzt. Unterschiede zwischen einzelnen Publikationen ergeben sich dadurch hauptsächlich durch die lokalen Bezüge wie Ortsnamen oder Namen der lokalen Abgeordneten.

Top5 Wörter in den Artikelüberschriften der Top6 Publikationen sortiert nach TF-IDF

# Filtern der Top6 Zeitungen
publisher_top6_header <- tb_header %>% add_count(publisher) %>% filter(dense_rank(-n) <= 6)
publisher_top6_header_count <- publisher_top6_header %>% count(publisher)

publisher_top6_header_plot <- publisher_top6_header %>%
  count(publisher, word, sort = TRUE) %>%
  bind_tf_idf(word, publisher, n) %>%
  mutate(word = fct_reorder(word, tf_idf))

publisher_top6_header_plot %>% 
  group_by(publisher) %>% 
  top_n(5, tf_idf) %>% 
  ungroup() %>%
  mutate(word = reorder(word, tf_idf)) %>%
  ggplot(aes(word, tf_idf, fill = publisher)) +
  scale_y_continuous(expand = c(0, 0)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y = "tf-idf") +
  facet_wrap(~publisher, ncol = 2, scales = "free") +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(fill= "Author", 
       title = "Häufigkeit nach TF-IDF (inverse Dokumenthäufigkeit)", 
       subtitle="Top5 Wörter in den Artikelüberschriften der Top6 Verlage mit den meisten Artikeln",
       x= NULL, 
       y= "tf-idf") +
  theme(plot.title = element_text(lineheight=.8, face="bold")) +
  scale_fill_brewer(palette="Set3")  


7.5. Analyse von Wortbeziehungen

Analyse der bigrams des Tagesspiegel

# Generieren der bigrams des Jahres 2017
bigrams_der_tagesspiegel <- presse_cleaned %>%
  filter(publisher == "der tagesspiegel") %>% 
  unnest_tokens(bigram, text, token = "ngrams", n = 2)

# Trennen der Wörter
bigrams_der_tagesspiegel_separated <- bigrams_der_tagesspiegel %>%
  separate(bigram, c("word1", "word2"), sep = " ")

# Filtern von Stopwords und fehlenden Werten
bigrams_der_tagesspiegel_filtered <- bigrams_der_tagesspiegel_separated %>%
  filter(!word1 %in% all_stopword$word) %>%
  filter(!word2 %in% all_stopword$word) %>% 
  filter(!str_detect(word1, pattern = "[[:digit:]]")) %>%
  filter(!str_detect(word2, pattern = "[[:digit:]]")) %>%
  filter(!is.na(word1))

# Anzahl der bigrams:
bigrams_der_tagesspiegel_counts <- bigrams_der_tagesspiegel_filtered %>% 
  count(word1, word2, sort = TRUE) %>% filter(n > 14)

# Filtern für geläufige Wortkombinationen
bigrams_der_tagesspiegel_graph <- 
  bigrams_der_tagesspiegel_counts %>%
  graph_from_data_frame()

Visualisierung der bigrams des Tagesspiegel mit ggraph

Die dargestellten bigrams kommen mindestens 15x in den Artikeln vor.

set.seed(1)

ggraph(bigrams_der_tagesspiegel_graph, layout = "fr") +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1)

Visualisierung der bigrams des Tagesspiegel mit ggraph (optimierte Settings)

set.seed(2)

arrow_set <- grid::arrow(type = "closed", length = unit(.15, "inches"))
ggraph(bigrams_der_tagesspiegel_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = arrow_set, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void()

Interpretation

Bei den Bigrams fällt auf, dass es sich sehr häufig um eine Kombination von Vor- und Nachnamen bekannter Politiker handelt. Auch häufig auftretende Wortkombinationen - z.B. ‘Hartz IV’, ‘rot-grün’ oder CDU-CSU - sind zu finden.

Analyse der bigrams des Jahres 2017

# Generieren der bigrams des Jahres 2017
bigrams_jahr_2017 <- presse_cleaned %>%
  filter(year == "2017")  %>% 
  unnest_tokens(bigram, text, token = "ngrams", n = 2)

# Trennen der Wörter
bigrams_jahr_2017_separated <- bigrams_jahr_2017 %>%
  separate(bigram, c("word1", "word2"), sep = " ")

# Filtern von Stopwords und fehlender Werte
bigrams_jahr_2017_filtered <- bigrams_jahr_2017_separated %>%
  filter(!word1 %in% all_stopword$word) %>%
  filter(!word2 %in% all_stopword$word) %>% 
  filter(!str_detect(word1, pattern = "[[:digit:]]")) %>%
  filter(!str_detect(word2, pattern = "[[:digit:]]")) %>%
  filter(!is.na(word1))

# Anzahl der bigrams
bigrams_jahr_2017_counts <- 
  bigrams_jahr_2017_filtered %>% 
  count(word1, word2, sort = TRUE) %>%
  filter(n > 39)

# Filtern der geläufigen Wortkombinationen
bigrams_jahr_2017_graph <- bigrams_jahr_2017_counts %>%
  graph_from_data_frame()

Visualisierung der bigrams des Jahres 2017 mit ggraph

Die dargestellten Token sind mindestens 40x in den Artikeln vertreten.

set.seed(3)

ggraph(bigrams_jahr_2017_graph, layout = "fr") +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1)

Visualisierung der bigrams des Jahres 2017 mit ggraph (optimierte Settings)

set.seed(4)

arrow_set <- grid::arrow(type = "closed", length = unit(.15, "inches"))

ggraph(bigrams_jahr_2017_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = arrow_set, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void()

Interpretation

Auch hier sind wieder ähnliche Muster wie oben zu erkennen. Auffällig ist die größere Baumstruktur, welche die Namen der beiden Spitzenkandidaten für das Kanzleramt miteinander verbindet.

Visualisierung der bigrams aller Jahre mit ggraph

bigrams_over_all_years <- 
  presse_cleaned %>%
  unnest_tokens(bigram, text, token = "ngrams", n = 2)

bigrams_over_all_years_separated <- 
  bigrams_over_all_years %>%
  separate(bigram, c("word1", "word2"), sep = " ")

# Filtern von Stopwords und fehlenden Werte
bigrams_over_all_years_separated_filtered <- 
  bigrams_over_all_years_separated %>%
  filter(!word1 %in% all_stopword$word) %>%
  filter(!word2 %in% all_stopword$word) %>% 
  filter(!str_detect(word1, pattern = "[[:digit:]]")) %>%
  filter(!str_detect(word2, pattern = "[[:digit:]]")) %>%
  filter(!is.na(word1))

bigrams_over_all_years_counts <- 
  bigrams_over_all_years_separated_filtered %>% 
  group_by(year,word1,word2) %>% 
  summarise(count = n()) %>% 
  ungroup() %>%
  rename(n = count) %>%
  filter(n > 49)

bigrams_over_all_years_graph <-
  graph_from_data_frame(bigrams_over_all_years_counts)

Bei der Darstellung behalten wir das Attribut “Jahr”, welches entsprechend der Verwendung in dem Jahr eingefärbt werden. Alle dargestellten Token wurden mindestens 50x in Artikeln genannt.

set.seed(6)

ggraph(bigrams_over_all_years_graph, layout = 'kk', maxiter = 100) + 
  geom_edge_link(aes(colour = factor(bigrams_over_all_years_counts$year))) + 
  geom_node_point() +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) + 
  scale_color_manual(name="Type of Activity") +
  theme(legend.title = element_blank(), legend.text = element_text(size = 14)) +
  coord_fixed()

Interpretation

Bei dieser Darstellung zeigt sich sehr schön, welche Personennamen im jeweiligen Wahljahr im Vordergrund standen. Vereinzelt lassen sich auch wieder bestimmte Themen - z.B. ‘Hartz’ und ‘Soziales’ im Jahr 2005 - zueinander zuordnen.

Analyse der Trigrams der Partei “CDU” im Jahr 2017

trigrams_year_2017 <- presse_cleaned %>%
  #filter(year == "2017")  %>% 
  unnest_tokens(trigram, text, token = "ngrams", n = 3)

trigram_texts_2017_separated <- trigrams_year_2017 %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ")

trigram_texts_2017_cdu_filtered <- trigram_texts_2017_separated %>%
  filter(!word1 %in% all_stopword$word) %>%
  filter(!word2 %in% all_stopword$word) %>%
  filter(!word3 %in% all_stopword$word) %>%
  filter(!str_detect(word1, pattern = "[[:digit:]]")) %>%
  filter(!str_detect(word2, pattern = "[[:digit:]]")) %>%
  filter(!str_detect(word3, pattern = "[[:digit:]]")) %>%  
  filter(word1 == "cdu" | word2 == "cdu" | word3 == "cdu")

trigram_texts_2017_cdu_counts <- trigram_texts_2017_cdu_filtered %>% 
  count(word1, word2, word3, sort = TRUE)

trigram_texts_2017_cdu_united <- trigram_texts_2017_cdu_counts %>%
  unite(trigram, word1, word2, word3, sep = " ") %>% top_n(10)

trigram_texts_2017_cdu_united %>%
  count(trigram, sort = TRUE) %>%
  mutate(trigram = reorder(trigram, n)) %>%
  ggplot(aes(trigram, n)) +
  geom_col() +
  xlab(NULL) +
  ylab("Häufigkeit (n)") + 
  coord_flip() +
  labs(title = "Trigrams aus dem Jahr 2017 mit dem Begriff 'CDU'") +
  theme_minimal()

Interpretation

Bei der Analyse der Trigrams stehen eindeutig Kombinationen von Personennamen und Parteien im Vordergrund. Insgesamt fällt auf, dass die Häufigkeit solcher Trigrams sehr niedrig ist.


7.6. Korrelation von Wortpaaren nach Publikation

Generieren von Wortpaaren

# Generieren von Wortpaaren nach Publikation
wortpaare_jahr_1990_count <- tb_text %>%
  filter(year == "1990")  %>% 
  pairwise_count(word, publisher, sort = TRUE)

# Generieren von Wortpaaren mit Korrelation (zwischen 0 und 1) nach Publikation
wortpaare_jahr_1998_count <- tb_text %>%
  filter(year == "1998")  %>% 
  pairwise_count(word, publisher, sort = TRUE)

Visualisierung von Wortpaaren mit Hilfe einer Netzwerkanalyse

Nachfolgend sind Wortpaare abgebildet, welche mehr als 4x zusammen genannt werden.

set.seed(7)

wortpaare_jahr_1990_n4 <- filter(wortpaare_jahr_1990_count, n >= 4)
wortpaare_jahr_1990_n4_graph <- graph_from_data_frame(wortpaare_jahr_1990_n4)

ggraph(wortpaare_jahr_1990_n4_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "cyan4") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE,
                 point.padding = unit(0.2, "lines")) +
  theme_void()

Interpretation

Diese Art der Darstellung zeigt sehr anschaulich, welche Begriffe sehr häufig und sehr häufig im Zusammenhang auftauchen. Diese Begriffe stehen im Zentrum der Visualisierung. Die weiter außen stehenden Begriffe tauchen ebenfalls häufig auf, Kombinationen mit den Wörtern im Zentrum sind allerdings etwas weniger häufig. Im konkreten Fall zeigt sich, dass die Parteien bei der Wahl 1990 im Zentrum der Berichterstattung standen. Allerdings spielte die DDR zu der Zeit noch eine wichtige Rolle in der Berichterstattung und auch das Datum der Wahl - der 2. Dezember 1990 ist zu erkennen.

Aufzeigen von Korrelationen des Begriffs “DDR”

In dem Aufruf werden die Items zunächst auf den Begriff “DDR” gefiltert, bevor diese in einem Balkendiagramm dargestellt werden.

wortpaare_jahr_1990_count %>%
  filter(item1 %in% "ddr") %>%
  filter(!str_detect(item2, pattern = "[[:digit:]]")) %>%
  top_n(1) %>%
  ungroup() %>%
  mutate(item2 = reorder(item2, n)) %>%
  ggplot(aes(item2, n)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ item1, scales = "free") +
  xlab(NULL) +
  ylab("Häufigkeit (n)") + 
  coord_flip() +
  labs(title = "Wortpaare aus dem Jahr 1990 mit dem Begriff 'DDR'") +
  theme_minimal()

Interpretation

Auch hier zeigen sich wieder relativ geringe Häufigkeiten, was vermutlich auch der geringen Anzahl von Presseberichten aus dem Jahr 1990 geschuldet ist. Deshalb ist die Aussagekraft dieser speziellen Analyse eher gering.

Visualisierung von Korrelationen der Begriffe “Internet” und “Digitalisierung”

Die Themen “Digitalisierung” und “Internet” waren im Jahr 1998 für den Wahlkampf und die Berichterstattung kaum relevant. Die nachfolgende Darstellung zeigt Wortpaare, welche mehr als 6x vorgekamen.

set.seed(8)

wortpaare_jahr_1998_internet <- filter(wortpaare_jahr_1998_count, item1 %in% c("internet", "digitalisierung", "digital")) %>% filter(n > 6)
wortpaare_jahr_1998_internet_graph <- graph_from_data_frame(wortpaare_jahr_1998_internet)

ggraph(wortpaare_jahr_1998_internet_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "cyan4") +
  geom_node_point(size = 5) +
  geom_node_text(aes(label = name), repel = TRUE,
                 point.padding = unit(0.2, "lines")) +
  theme_void()

Interpretation

In dieser Darstellung lässt sich gut erkennen, welche anderen Begriffe häufig im Umfeld eines bestimmten Themas wiederfinden. Dadurch kann man im Idealfall Rückschlüsse ziehen, welche Parteien oder Politiker sich zu diesem Thema äußern.